package com.lins.store.controller;

import com.lins.store.bo.UserService;
import com.lins.websocket.WsServer;
import com.lins.store.dto.ChangePasswordDTO;
import com.lins.store.dto.LoginDTO;
import com.lins.store.dto.RegisterDTO;
import com.lins.store.entity.UserEntity;
import com.lins.store.entity.ex.PasswordNotMatchException;
import com.lins.store.entity.ex.UserNotFoundException;
import com.lins.common.util.JsonResult;
import com.lins.common.util.ObjectCheck;
import com.lins.common.util.SessionUtils;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.StringUtils;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;

/**
 * @Author linshuo
 * @Date 2023/8/10 17:05
 */
@RestController
@RequestMapping("login")
@Api(value = "登录接口",tags = {"登录接口"}) //接口名称和标签名
public class LoginController extends BaseController{

    @Autowired
    private UserService userService;

    @Autowired
    private WsServer wsServer;

    @SuppressWarnings("deprecation")
    @PostMapping("reg")
    @ApiOperation("用户注册")
    public JsonResult<String> reg(RegisterDTO reg) {
        //非空约束验证不通过
        if (!ObjectCheck.validateProperty(reg)) throw new UserNotFoundException("注册信息不完整");
        if (!reg.getPassword().equals(reg.getPassword2())) throw new PasswordNotMatchException("两次密码不一致");
        return new JsonResult<>(OK,userService.reg(reg));
    }

    @SuppressWarnings("deprecation")
    @PostMapping("sendPhoneCode")
    @ApiOperation("发送手机验证码")
    public JsonResult<String> sendPhoneCode(RegisterDTO reg) {
        userService.sendPhoneCode(reg);
        return new JsonResult<>(OK);
    }



    @SuppressWarnings("deprecation")
    @PostMapping("login")
    @ApiOperation("用户登录")
    public JsonResult<UserEntity> login(@Validated LoginDTO dto, HttpSession session) {
        //非空约束验证不通过    2023/8/15优化：使用注解方式
//        if (!ObjectCheck.validateProperty(dto)) throw new UserNotFoundException("登录信息不完整");
        /**
         * 其实需要HttpSession session = request.getSession();只是mvc框架帮我们做了这步，session已经被声明，可直接使用。
         * client第一次访问server，会在server生成一个session，有sessionId与之对应(tomcat生成的叫做jsessionid)
         * server将sessionId返回给client，并在client的cookie保存sessionId,下次请求会被client携带上，用来获取session
         * 关闭浏览器服务器也不会销毁session，session销毁只能通过invalidate()或过期超时
         * 注销登录会销毁session对象，登录时会再生成session对象
         * 若不注销，重新登录，会带旧sessionID请求，并刷新session活性，直至session过期。
         * 若session没有销毁，客户端带旧的sessionID来，只是能在server得到对应的session，表示已经登录。
         * 流程：
         * 第一次登录，给session赋值，生成sessionID,返回给cookie保存。发请求时，在session有效期内，免登录。
         * 第二次换号登录时，仍然从cookie携带旧的sessionID，server根据ID能找到对应的session，误以为还是老用户。
         * 因为session对象仍然存在，只是重新给session内容赋值，仍使用旧的sessionID
         * 第一个页面再携带它的sessionID发请求，对应的session存在，免登录。只是session内容(uid)已经被第二个用户覆盖了。
         * 第一个页面，再次操作，其实拿的是覆盖后的uid，也就是第二个用户权限下的事情。这是很危险的。
         * 若session被注销，则旧页面带旧sessionID在server找不到session，返回异常。前端跳转登录页。
         * 解决办法：
         * 1，登录时判断该sessionId在server内是否存在session，存在则不允许登录。除非换浏览器，即换sessionId。
         */

        //如果老的session仍然存在，则无须登录，直接到首页
        if (null != session.getAttribute("uid")) return new JsonResult<>(OK,new UserEntity("99"));
        UserEntity data = userService.login(dto,session);

        /**
         * 在同一个浏览器下(包括不同的Tab)访问同一个域名，浏览器会从cookie查找并携带该域名分发的sessionId发出请求
         * 服务器拿到sessionId后，查找对应的session对象，作为当前连接的会话。
         * 假如有用户在该浏览器第二次访问服务器，因已经存在有sessionId，服务器不会再生成，而是查找出来。
         * 但查找出来的这个session其实是上一个用户的，当前用户再次将自己的数据放入该session，且key一样，就会把上个用户的session内数据覆盖掉。
         * 解决问题的源头是，阻止浏览器携带错误的sessionId去访问服务器。即在同一浏览器下不可同时登录2个账号。否则后者会使用前者的session。
         **/

        session.setAttribute("uid",data.getUid());
        session.setAttribute("username",data.getUsername());

        SessionUtils instance = SessionUtils.getInstance();
        instance.addSession(data.getUid(),session);

        /**
         * 有用户登录上线，可根据管理员uid获取到session,用ws通知管理员，xx用户上线了
         **/
        if (data.getUid() != 1){
            HttpSession adminSession = instance.getSession(1);
            if (adminSession != null){
                wsServer.sendMessage(adminSession.getId(),"用户"+data.getUsername()+"上线啦");
            }
        }
        //取出一会要跳转的路径
        String oldURL = (String) session.getAttribute("oldURL");
        if (StringUtils.hasText(oldURL)) {
            data.setAvatar(oldURL);
            //清空当前session的key，防止下次乱跳
            session.setAttribute("oldURL",null);
        }
        return new JsonResult<>(OK, data);
    }

    @PostMapping("change_password")
    @ApiOperation("修改密码")
    public JsonResult<Void> changePassword(ChangePasswordDTO dto, HttpSession session) {
        dto.setUid(getUidFromSession(session));
        dto.setUsername(getUsernameFromSession(session));
        userService.changePassword(dto);
        return new JsonResult<>(OK);
    }

    @PostMapping("logout")
    @ApiOperation("注销登录")
    public JsonResult<Void> logout(HttpSession session) {
        //若开启“不允许同时登录”则注销时应该将账户状态改为0-正常
        userService.logout(Integer.parseInt(getUidFromSession(session)));
        //将当前session设置为失效，调用get方法会报空指针
        session.invalidate();
        return new JsonResult<>(OK);
    }

    /**
     * 在用
     **/
    @GetMapping("getImage")
    @ApiOperation("获取图片验证码")
    public void getImage(HttpSession session, HttpServletResponse response) throws IOException {
        userService.getImage(session,response);
    }

    /**
     * 数字有些过于清晰，未在用
     **/
    @GetMapping("getYzmImage")
    @ApiOperation("获取验证码图片")
    public void getYzmImage(HttpServletRequest request, HttpServletResponse response) {
        userService.getYzmImage(request,response);
    }


    /**
     * 强制用户下线，注销用户session，被监听者改变用户为离线状态
     **/
    @GetMapping("offline/{uid}")
    @ApiOperation("强制用户下线")
    public void offline(@PathVariable("uid") String uid){
        HttpSession session = SessionUtils.getInstance().getSession(Integer.parseInt(uid));
        //销毁该用户的session
        session.invalidate();
        //给这个用户发消息
        wsServer.sendMessage(session.getId(),"你被管理员强制下线！");
    }

}
